﻿#region using

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

#endregion

namespace JupiterLibrary
{
    /// <summary>
    /// Representa un VertexBuffer con unas propiedades específicas ya preasignadas que el usuario puede asignar fácilmente, como el vertex color.
    /// El objeto que representa es en 2D, no en 3D. Sin embargo el VertexBuffer que se crea sí es en 3D.
    /// </summary>
    public class VB
    {
        public Vector3[] Position;

        public Color[] Color;

        public Vector2[] TextureCoord;

        public Vector3[] Normal;

        public bool HasColor { get { return Color != null; } }

        VertexBuffer vertexBuffer;

        VDecl declaration;

        /// <summary>Informs the engine that this buffer is dirty and needs to update. The engine will build a new declaration if it's needed</summary>
        public bool Dirty=true;

        public void Dispose()
        {
            if (vertexBuffer != null)
            {
                vertexBuffer.Dispose();

                vertexBuffer = null;

                // We do not set declaration=null because other VB could use a declaration like this
            }
        }

        public void SetAlpha(float alpha)
        {
            for (int i = 0; i < Color.Length; i++)
            {
                Color[i].A = (byte)(alpha*255f);
            }
        }

        public void SetColor(Color color)
        {
            for (int i = 0; i < Color.Length; i++)
                Color[i] = color;
        }

        public void SetColorWithoutAlpha(Color color)
        {
            for (int i = 0; i < Color.Length; i++)
            {
                byte a = Color[i].A;

                Color[i] = color;

                Color[i].A = a;
            }
        }

        /// <summary>
        /// Binds the vertexbuffer to the Device. Update is called in this method before binding the buffer
        /// </summary>
        internal void SetToDevice()
        {
            Update();

            Jupiter.Video.GraphicsDevice.SetVertexBuffer(vertexBuffer);
        }

        /// <summary>
        /// Apply a matrix to Position and Normal vectors
        /// </summary>
        /// <param name="matrix"></param>
        public void ApplyMatrix(Matrix matrix)
        {
            for (int i = 0; i < Position.Length; i++)
            {
                Position[i] = Vector3.Transform(Position[i], matrix);
            }

            if (Normal != null)
            {
                for (int i = 0; i < Normal.Length; i++)
                {
                    Normal[i] = Vector3.Transform(Normal[i], matrix);
                }
            }
        }

        /// <summary>
        /// Upddate the internal vertexbuffer with newvalues if needed
        /// </summary>
        public void Update()
        {
            if (!Dirty) return;
            // Check if we need a new declaration

            VDecl declarationTmp = Jupiter.Video.VertexDeclarations.GetDeclaration(this);

            // A new one, so we need a new VertexBuffer too
            if (declarationTmp != declaration)
            {
                declaration = declarationTmp;

                // Dispose the last vertexbuffer

                Dispose();
            }

            // Comprobamos si ha cambiado el taño del vb

            if (vertexBuffer != null)
                if (Position.Length != vertexBuffer.VertexCount)
                    Dispose();

            Dirty = false;

            if (vertexBuffer == null)
            {
                vertexBuffer = new VertexBuffer(Jupiter.Video.GraphicsDevice, declaration.VertexDeclaration, Position.Length, BufferUsage.WriteOnly);
            }

            int offsetInBytes = 0;

            // Position

            vertexBuffer.SetData<Vector3>(offsetInBytes, Position, 0, Position.Length, declaration.VertexDeclaration.VertexStride);

            offsetInBytes = 3 * sizeof(float);

            // Color

            if (Color != null)
            {
                vertexBuffer.SetData<Color>(offsetInBytes, Color, 0, Color.Length, declaration.VertexDeclaration.VertexStride);

                offsetInBytes += 4; // ARGB=32 bits
            }

            // Normal

            if (Normal != null)
            {
                vertexBuffer.SetData<Vector3>(offsetInBytes, Normal, 0, Normal.Length, declaration.VertexDeclaration.VertexStride);

                offsetInBytes += 3 * sizeof(float);
            }

            // TextureCoord

            if (TextureCoord != null)
            {
                vertexBuffer.SetData<Vector2>(offsetInBytes, TextureCoord, 0, TextureCoord.Length, declaration.VertexDeclaration.VertexStride);

                offsetInBytes += 2 * sizeof(float);
            }

        }
    }

    /// <summary>
    /// Gestiona la colección de <see cref="VB"/>.
    /// </summary>
    internal class VertexBuffers
    {

        public List<VB> List;

        public VertexBuffers()
        {
            List = new List<VB>();
        }

        /// <summary>
        /// Dispose and removes the list elements
        /// </summary>
        public void Dispose()
        {
            foreach (VB vb in List)
            {
                vb.Dispose();
            }

            List.Clear();
        }

        public VB CreateAndAdd()
        {
            VB vb = new VB();
            
            List.Add(vb);

            return vb;
        }
    }
}
